using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using ProjectEuler.Core;

namespace ProjectEuler.UI
{
    // this class encapsulates the ThreadPool and provides some functionality
    // revolving around the NumberProblemsSolved (which gets incremented by each
    // thread that's done getting a problem answer)
    public class EulerThreadPool
    {
        private const int MAX_THREADS = 20;
        private readonly ConcurrentQueue<SolvedProblem> SolvedProblems;
        private readonly IList<IProjectEulerProblem> Problems;
        private int NumberProblemsSolved;

        public EulerThreadPool()
        {
            SolvedProblems = new ConcurrentQueue<SolvedProblem>();
            Problems = new List<IProjectEulerProblem>();
        }

        public void AddProblem(IProjectEulerProblem problemInstance)
        {
            Problems.Add(problemInstance);
        }

        private void SolveProblem(IProjectEulerProblem problem)
        {
            string answer = "Exception, unable to solve.";
            var watch = new Stopwatch();
            try
            {
                watch.Start();
                answer = problem.Answer;
                watch.Stop();
            }
            catch (Exception ex)
            {
                ExceptionForm.ShowException(ex);
            }
            finally
            {
                SolvedProblems.Enqueue(new SolvedProblem
                                           {
                                               Answer = answer,
                                               Description = problem.Description,
                                               Number = problem.Number,
                                               Runtime = new TimeSpan(watch.ElapsedTicks)
                                           });
                Interlocked.Increment(ref NumberProblemsSolved);
            }
        }


        public void StartSolving()
        {
            ThreadPool.SetMaxThreads(MAX_THREADS, 0);
            foreach (var problem in Problems)
            {
                var prob = problem;
                ThreadPool.QueueUserWorkItem(context => SolveProblem(prob));
            }
        }

        public bool AnyThreadsStillRunning
        {
            get { return NumberProblemsSolved < Problems.Count; }
        }

        public int NumberProblemsSolvedSoFar
        {
            get { return NumberProblemsSolved; }
        }

        public bool AnySolvedProblemsInQueue
        {
            get { return SolvedProblems.Count > 0; }
        }

        public SolvedProblem GetSolvedProblemFromQueue
        {
            get
            {
                SolvedProblem solvedProblem;
                // this will not throw an exception because this is the only place
                // that it is getting dequeued, so that's why there's no try/catch here
                SolvedProblems.TryDequeue(out solvedProblem);
                return solvedProblem;
            }
        }
    }
}